package EventPackage;


import java.util.Vector;

public class EventDispatcher extends Thread {

    public static final int DEFAULT_RETRY_INTERVAL = 1000; // not relevant - by default there are no retries
    public static final int DEFAULT_TIME_TO_LIVE = 0; // no retries

    private Vector<EventWrapper> queue;

    private RMIEventManagerInterface em; // Event manager interface object

    private boolean running = true;

    // time between sends attempts when the communication is down
    private int retry_interval;
    // time to live for events - after this no more retries are sent
    // 0 means that only one attempt will be done (no retry scheme)
    private int time_to_live;

    public EventDispatcher(RMIEventManagerInterface em) {
        this(em,DEFAULT_RETRY_INTERVAL,DEFAULT_TIME_TO_LIVE);
    }

    public EventDispatcher(RMIEventManagerInterface em, int retry_interval, int time_to_live) {
        this.em = em;
        this.retry_interval = retry_interval;
        this.time_to_live = time_to_live;
        this.queue= new Vector<EventWrapper>();
    }

    /**
     * Method add ...
     *
     * @param event of type Event
     */
    public synchronized void add(Event event) {
        this.queue.add(new EventWrapper(event));
        this.notifyAll();
    }

    /**
     * Method addInHead ...
     *
     * @param event of type EventWrapper
     */
    private synchronized void addInHead(EventWrapper event) {
        this.queue.add(0, event);
    }

    /**
     * Method get ...
     * @return Event
     */
    public synchronized EventWrapper get() {
        while (this.queue.isEmpty() && this.running) {
            try {
                this.wait(5000);
            } catch (InterruptedException e) {
            }
        }

        if (!this.running) {
            return null;
        }

        return this.queue.remove(0);
    }

    /**
     * Method run ...
     */
    public void run() {
        while (running) {

            EventWrapper event = this.get();

            if (event == null) {
                continue;
            }

            try {
                this.logEvent("Attempting to send event", event.getEvent());
                em.SendEvent(event.getEvent());
            } catch (Exception e) {
                // event sending failed - adding to head for retry
                this.logEvent("Sending event failed", event.getEvent());
                long now = System.currentTimeMillis();
                // if expiration time not reached yet, put back in top of the queue
                if (event.getTs() + this.time_to_live > now) {
                    this.addInHead(event);
                } else {
                    this.logEvent("Event expired", event.getEvent());
                }
                try {Thread.sleep(this.retry_interval);} catch (InterruptedException ie) {}
            }

        }
    }

    private void logEvent(String msg, Event e) {
        if (EventManagerInterface.mw != null) {
            EventManagerInterface.mw.WriteMessage("[EventDispatcher]" + msg + "[ID="+e.GetEventId() + " | MESSAGE="+e.GetMessage()+"]");
        }
    }

    public void setRunning(boolean running) {
        this.running = running;
    }

    private class EventWrapper {

        private Event event;
        private long ts;

        private EventWrapper(Event event) {
            this.event = event;
            this.ts = System.currentTimeMillis();
        }

        public Event getEvent() {
            return event;
        }

        public long getTs() {
            return ts;
        }
    }
}
